package sdk;

import java.util.ArrayList;
import java.util.Arrays;

public class Field
{
	private String[][] field = new String[9][9];
	private String[][] fieldOrig = new String[9][9];
	private Element[][] elements = new Element[9][9];
	private Square[][] squares = new Square[3][3];

	public Field(String[][] field)
	{
		this.field = field;
		this.fieldOrig = field;
		elementsUpdate();
	}

	public Field(String pattern)
	{
		String[][] field = getFieldFrompattern(pattern);
		this.field = field;
		this.fieldOrig = field;
		elementsUpdate();
	}

	private String[][] getFieldFrompattern(String pattern)
	{
		String[][] res = new String[9][9];
		String[] s = pattern.split(",");
		for (int k = 0; k < s.length; k++)
		{
			int r = k / 9;
			int c = k % 9;
			res[r][c] = (s[k].equalsIgnoreCase("0") ? "*" : s[k]);

		}
		return res;
	}

	private void elementsUpdate()
	{
		VectorSDK[] rows = new VectorSDK[9];
		VectorSDK[] cols = new VectorSDK[9];
		for (int i = 0; i < 9; i++)
		{
			rows[i] = new VectorSDK(field[i]);
		}
		String[][] fieldT = new String[9][9];
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				fieldT[i][j] = field[j][i];
			}
		}

		for (int i = 0; i < 9; i++)
		{
			cols[i] = new VectorSDK(fieldT[i]);
		}

		// TODO set SQUARES
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				Element e = new Element(field[i][j], i, j);
				VectorSDK rr = rows[i];
				rr.setxPos(i);
				rr.setyPos(j);

				VectorSDK cc = cols[j];
				cc.setxPos(i);
				cc.setyPos(j);

				e.setRow(rr);
				e.setColumn(cc);
				e.setSquare(new Square());
				elements[i][j] = e;
			}
		}

		Square[][] qM = new Square[3][3];
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				qM[i][j] = new Square();
			}
		}

		for (int i = 0; i < 9; i = i + 3)
		{
			for (int j = 0; j < 9; j = j + 3)
			{

				for (int r = 0; r < 3; r++)
				{
					for (int c = 0; c < 3; c++)
					{
						// System.out.println("["+i+"*"+r+"]["+j+"*"+
						// c+"] = ["+(i+r)+"*"+ (j+c)+"] = ");
						qM[i / 3][j / 3].square[r][c] = field[i + r][j + c];
					}

				}
			}

		}

		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				elements[i][j].setSquare(qM[i / 3][j / 3]);
			}
		}

		this.printField();
		this.makeHypo();
		System.out.println("");

	}

	private void printField()
	{
		String r = "+ ";
		for (int i = 0; i < 9; i++)
		{
			r = r + i + " ";
			if ((i + 1) % 3 == 0)
				r = r + " ";
		}
		r = r + "\n";
		for (int i = 0; i < 9; i++)
		{
			r = r + "--";
			if ((i + 1) % 3 == 0)
				r = r + "-";
		}
		r = r + "\n";
		for (int i = 0; i < 9; i++)
		{
			r = r + i + "|";
			for (int j = 0; j < 9; j++)
			{
				r = r + field[i][j] + " ";
				if ((j + 1) % 3 == 0)
					r = r + " ";
			}
			r = r + "\n";
			if ((i + 1) % 3 == 0)
				r = r + "\n";
		}
		System.out.println(r);
		int a = 0;

	}

	public void makeHypo()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Element e = elements[r][c];
				e.elaborateGuess();
				// System.out.println(e);
				e.printGuess(-1);
			}

		}

	}

	public void solve() throws Exception
	{
		int singletonCy = 0;
		int duplicateCy = 0;
		if (!this.isSolved() && this.hasSingleton())
		{
			do
			{
				this.solveSingleton();
				this.makeHypo();
				singletonCy++;
				printField();
				System.out.println("Solve Singleton: " + singletonCy + " Step");
			}
			while (this.hasSingleton());
		}

		if (!this.isSolved())
		{
			do
			{
				this.solveSingleton3D();
				this.solveDuplicates();
				this.solveSingleton();
				this.makeHypo();
				duplicateCy++;
				System.out.println("Solve Duplicates: " + duplicateCy + " Step");
			}
			while (this.hasSingleton());
		}

	}

	private void solveSingleton3D()
	{
		printField();
		printAllGuess();
		solveRowGuess();
		solveColGuess();
		solveSquareGuess();
	}

	private void solveSquareGuess()
	{

		String[][] squareGuess = new String[9][9];
		boolean quareToChange = true;
		do
		{
			quareToChange = false;
			// cycle for each square
			for (int i = 0; i < 3; i++)
			{
				for (int j = 0; j < 3; j++)
				{
					int r=0;
					int c = 0;
					// cycle for each cell in square
					int index=0;
					for (int row = 0; row < 3; row++)
					{
						for (int col = 0; col < 3; col++)
						{
							r = i * 3 + row;
							c = j * 3 + col;
							squareGuess[index++] = elements[r][c].getGuess().getVectorSDK();
							System.out.println(elements[r][c].getGuessLine());
						}
					}

					ArrayList<String> guessSquareArray = guessFromArray(squareGuess);
					String aaa="-";
					for(String s :  guessSquareArray)
						aaa=aaa+s+" ";
					printAllGuess();
					System.out.println("["+i+","+j+"]" + aaa+"-");
					if (guessSquareArray.size() > 0)
					{
						for (String s : guessSquareArray)
						{
							System.out.println("Col:" + c + " s:" + s);
							String[] parts = s.split(",");
							field[Integer.parseInt(parts[1])][c] = parts[0];
							quareToChange = true;
						}
					}

				}
			}
			this.makeHypo();
			printField();
			printAllGuess();
		}
		while (quareToChange);
		System.out.println("End Squares");

	}

	private void solveColGuess()
	{
		String[][] cellGuess = new String[9][9];
		boolean cellToChange = true;
		do
		{
			cellToChange = false;
			for (int r = 0; r < 9; r++)
			{
				System.out.println("Row::" + r);
				for (int c = 0; c < 9; c++)
				{

					cellGuess[c] = elements[r][c].getGuess().getVectorSDK();
					System.out.println(elements[r][c].getGuessLine());
				}
				ArrayList<String> guessRowArray = guessFromArray(cellGuess);

				if (guessRowArray.size() > 0)
				{
					for (String s : guessRowArray)
					{
						System.out.println("Row:" + r + " s:" + s);
						String[] parts = s.split(",");
						field[r][Integer.parseInt(parts[1])] = parts[0];
						cellToChange = true;
					}
				}
			}
			this.makeHypo();
			printField();
			printAllGuess();
		}
		while (cellToChange);
		System.out.println("End Cell");

	}

	private void solveRowGuess()
	{
		String[][] rowGuess = new String[9][9];
		boolean rowToChange = true;
		do
		{
			rowToChange = false;
			for (int c = 0; c < 9; c++)
			{
				// System.out.println("Col:"+c);
				for (int r = 0; r < 9; r++)
				{
					rowGuess[r] = elements[r][c].getGuess().getVectorSDK();
					// System.out.println(elements[r][c].getGuessLine());
				}
				ArrayList<String> guessRowArray = guessFromArray(rowGuess);

				if (guessRowArray.size() > 0)
				{
					for (String s : guessRowArray)
					{
						System.out.println("Col:" + c + " s:" + s);
						String[] parts = s.split(",");
						field[Integer.parseInt(parts[1])][c] = parts[0];
						rowToChange = true;
					}
				}
			}
			this.makeHypo();
			printField();
			printAllGuess();
		}
		while (rowToChange);
		System.out.println("End Rows");

	}

	private ArrayList<String> guessFromArray(String[][] rowGuess)
	{
		ArrayList<String> res = new ArrayList<String>();
		for (int col = 0; col < 9; col++)
		{
			int count = 0;
			String colTest = (col + 1) + "";
			int rowSingle = -1;
			for (int row = 0; row < 9; row++)
			{
				String val = rowGuess[row][col];
				//System.out.println("c:"+col+" , r:"+row+" val:"+val);
				if (val != null && val.equalsIgnoreCase(colTest))
				{
					count++;
					rowSingle = row;
				}
			}
			if (count == 1)
				res.add((col + 1) + "," + rowSingle);
		}

		return res;
	}

	private void solveDuplicates()
	{
		printField();
		printAllGuess();

	}

	private void printAllGuess()
	{
		String res = ".  ";
		for (int r = 0; r < 9; r++)
		{
			res = res + "-----" + r + "-----  ";
			if ((r + 1) % 3 == 0)
				res = res + " ";
		}
		res = res + "\n";

		for (int r = 0; r < 9; r++)
		{
			res = res + r + "|";
			for (int c = 0; c < 9; c++)
			{
				Element e = elements[r][c];
				res = res + e.getGuessLine() + " ";
				if ((c + 1) % 3 == 0)
					res = res + " ";
			}
			res = res + "\n";
			if ((r + 1) % 3 == 0)
				res = res + "\n";

		}
		System.out.println(res);
	}

	private void solveSingleton() throws Exception
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Element e = elements[r][c];
				if (e.getHowManyGuess() == 1)
				{
					String val = e.getSingleGuess();
					if (val != null)
					{
						field[r][c] = val;
					}
					else
					{
						throw new Exception("Singleton guess can't have more the one value cell = [" + r + "," + c + "]");
					}
				}

			}

		}

		elementsUpdate();

	}

	/**
	 * return true id every guess vector has guessNumber==0
	 * 
	 * @return
	 */
	private boolean isSolved()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Element e = elements[r][c];
				if (e.getHowManyGuess() != 0)
					return false;
			}

		}
		return true;
	}

	private boolean hasSingleton()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Element e = elements[r][c];
				if (e.getHowManyGuess() == 1)
					return true;
			}

		}
		return false;
	}
}
